import com.runa.protocol.enums.ByteOrder;
import com.runa.protocol.util.ByteUtil;
import com.runa.protocol.util.DigitalUtil;
import org.junit.Test;

import java.util.*;

/**
 * 解码器，调用时构造后直接使用decode函数，传入16进制报文
 */
public class MessageDecode {

    public EquipmentData decode(String hexString) {
        EquipmentData equipmentData = new EquipmentData();

        byte[] bytes = ByteUtil.hexStringToByte(hexString);
        equipmentData.setSerialNo(ByteUtil.byteToHexString(bytes, 5, 4));
        int len = ByteUtil.byteToShort(bytes, 1, ByteOrder.AB);
        int index = 10;
        do {
            int type = bytes[index++];
            int dataLen = (bytes[index++] & 0xff) - 10;
            Date date = toDate(bytes, index);
            index += 6;
            String serial = ByteUtil.byteToHexString(bytes, index, 4);
            index += 4;
            switch (type) {
                case 1:
                    Displacement displacement = new Displacement(serial, date);
                    do {
                        displacement.getTimeFrameData().add(
                                new Displacement.DataPair(bytes[index++],
                                        ByteUtil.byteToShort(bytes, index, ByteOrder.AB)));
                        index += 2;
                        dataLen -= 3;
                    } while (dataLen > 1);
                    displacement.setStatus(bytes[index++]);
                    equipmentData.getDisplacements().add(displacement);
                    break;
                case 3:
                    LeakThrough leakThrough = new LeakThrough(serial, date);
                    do {
                        leakThrough.getTimeFrameData().add(
                                new LeakThrough.DataPair(bytes[index++],
                                        (float) (ByteUtil.byteToShort(bytes, index, ByteOrder.AB) * 0.1),
                                        (float) (ByteUtil.byteToShort(bytes, index+2, ByteOrder.AB) * 0.1),
                                        (float) (ByteUtil.byteToShort(bytes, index+4, ByteOrder.AB) * 0.1)));
                        index += 6;
                        dataLen -= 7;
                    } while (dataLen > 1);
                    leakThrough.setStatus(bytes[index++]);
                    equipmentData.getLeakThroughs().add(leakThrough);
                    break;
                case 2:
                    LiquidLevel liquidLevel = new LiquidLevel(serial, date);
                    do {
                        liquidLevel.getTimeFrameData().add(new LiquidLevel.DataPair(
                                bytes[index++], bytes[index++] & 0xff));
                        dataLen -= 2;
                    } while (dataLen > 1);
                    liquidLevel.setStatus(bytes[index++]);
                    equipmentData.getLiquidLevels().add(liquidLevel);
                    break;
                default:
            }
        } while (len > index + 14);
        index += 2;
        equipmentData.setDate(toDate(bytes, index));
        index += 6;
        equipmentData.setVoltage((float) (ByteUtil.byteToShort(bytes, index, ByteOrder.AB) * 0.01));
        index += 2;
        equipmentData.setSignal(bytes[index++] & 0xff);
        equipmentData.setTemperature((float) (ByteUtil.byteToShort(bytes, index, ByteOrder.AB) * 0.01));
        index += 2;
        equipmentData.setSIM(ByteUtil.byteToHexString(bytes, index, 10));
        return equipmentData;
    }

    private Date toDate(byte[] bytes, int offset) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2000+bytes[offset], bytes[offset+1], bytes[offset+2], bytes[offset+3], bytes[offset+4], bytes[offset+5]);
        return calendar.getTime();
    }

    /**
     * 示例方法
     * @param args
     */
    public static void main(String[] args) {
        MessageDecode decode = new MessageDecode();
//        EquipmentData equipmentData = decode.decode("68004b68010000000304010e140b131103281000101001017000020d140b1311032d000000020139000312140b13110332000000030100d300be010c000409140b1311050e01731407fc898611182830080502541016");
//        System.out.println(equipmentData);
        System.out.println(String.format("%8s", Integer.toHexString(Float.floatToIntBits((float) 0))).replaceAll(" ","0"));
    }

    @Test
    public void base64() {
        String base = "EgH9ARIBAgISAQcCEQEIAhEBCwIRAQ0CEAEIAhABEAIQARYCEAEZAhABHQIPAR8CDwEiAg4BJAIOASQCDQEiAg==";
        byte[] bytes = Base64.getDecoder().decode(base);
        String hex = ByteUtil.byteToHexString(bytes);
        System.out.println(hex);
        System.out.println(bytes.length);

        int index = 0;
        List<wenshidu> list = new ArrayList<>();
        while (index < bytes.length) {
            list.add(new wenshidu(byteToShort(bytes, index, false)/10, byteToShort(bytes, index+2, false)/10));
            index += 4;
        }
        System.out.println(list);
    }

    static class wenshidu {
        float wendu;
        float shidu;

        public wenshidu(float wendu, float shidu) {
            this.wendu = wendu;
            this.shidu = shidu;
        }

        @Override
        public String toString() {
            return "wenshidu{" +
                    "wendu=" + wendu +
                    ", shidu=" + shidu +
                    '}';
        }
    }


    public List<wenshidu> decoder(String base64) {
        byte[] bytes = Base64.getDecoder().decode(base64);
        List<wenshidu> list = new ArrayList<>();
        int index = 0;
        while (index < bytes.length) {
            list.add(new wenshidu(byteToShort(bytes, index, false)/10, byteToShort(bytes, index+2, false)/10));
            index += 4;
        }
        return list;
    }

    public static short byteToShort(byte[] b, int offSet, boolean order) {
        if(order){
            return (short)(((b[offSet]& 0xff)<<8) | (b[offSet+1]& 0xff));
        }else{
            return (short)(((b[offSet+1]& 0xff)<<8) | (b[offSet]& 0xff));
        }
    }

    public String paymentNumber(String equID, float money, float price) {
        String moneyStr = String.format("%8s", Integer.toHexString(Float.floatToIntBits(money)).toUpperCase()).replaceAll(" ", "0");
        String priceStr = String.format("%8s", Integer.toHexString(Float.floatToIntBits(price))).replaceAll(" ", "0");
        String stepOne = equID + System.currentTimeMillis() / 1000 + moneyStr + priceStr;
        int crc = getCRC(hexStringToByte(stepOne));
        return stepOne + byteToHexString(new byte[]{(byte) (crc + 0x33 ^ 0x5a), (byte) ((crc >> 8) + 0x33 ^ 0x5a)});
    }

    public byte[] hexStringToByte(String str) {
        str = str.replaceAll(" ", "");
        byte[] bytes = new byte[str.length() / 2];
        for(int i = 0; i < str.length() / 2; i++) {
            String subStr = str.substring(i * 2, i * 2 + 2);
            bytes[i] = (byte) Integer.parseInt(subStr, 16);
        }
        return bytes;
    }

    public String byteToHexString(byte[] b){
        StringBuilder sb = new StringBuilder();
        for (byte b1 : b) {
            sb.append(String.format("%02X", b1));
        }
        return sb.toString();
    }

    public int getCRC(byte[] bytes) {
        int CRC = 0xffff;
        int POLYNOMIAL = 0xa001;

        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= ((int) bytes[i] & 0xff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 1) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        return CRC;
    }

    @Test
    public void test001() {
        System.out.println(0x11 & 0x7f);
        System.out.println(0x13 & 0x3f);
        System.out.println(0xc6 & 0x3f);
        System.out.println(0x21 & 0xf);
        System.out.println((0x21 >> 1 & 0x38) | (0xc6 >>> 5));
        System.out.println(date(new byte[]{0x11, 0x13, (byte) 0xc6, 0x21}, 0));
    }

    public String date(byte[] bytes, int start) {
        int minute = bytes[start] & 0x7f;
        int hour = bytes[start+1] & 0x3f;
        int day = bytes[start+2]  & 0x3f;
        int month = bytes[start+3] & 0xf;
        int year = ((bytes[start+3] & 0xf0) >> 1) | ((bytes[start+2] & 0xe0) >> 5);
        return "20"+year+month+day+hour+minute;
    }

}
